package minesweeper;

import java.awt.GridLayout;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JPanel;

import minesweeper.Cell.Status;

public class Grid extends JPanel implements MouseListener  {
	
	enum winCondition{
		WON,
		FAILED,
		UNCONFIRMED,
	}

	public Grid(){
		mineSet = false;
		winStatus = winCondition.UNCONFIRMED;
		width = 9;
		height = 9;
		numOfMines =10;
		flagsLeft = numOfMines;
		
		freeSpaces = (width * height) - numOfMines;
		setLayout(new GridLayout(height, width));
		cell = new Cell[height][width];
		for(int y = 0;y < height; y++){
			for(int x = 0; x< width; x++){
				cell[y][x] = new Cell(x, y);
				cell[y][x].addMouseListener(this);
				add(cell[y][x]);
			}
		}
	}
	
	public Grid(int width, int height, int numOfMines){
		mineSet = false;
		winStatus = winCondition.UNCONFIRMED;
		this.width = width;
		this.height = height;
		this.numOfMines = numOfMines;
		flagsLeft = numOfMines;
		
		freeSpaces = (width * height) - numOfMines;
		setLayout(new GridLayout(height, width));
		cell = new Cell[height][width];
		for(int y = 0;y < height; y++){
			for(int x = 0; x< width; x++){
				cell[y][x] = new Cell(x, y);
				cell[y][x].addMouseListener(this);
				add(cell[y][x]);
			}
		}
	}
	
	
	public Grid(String arg){
	    String[] args = arg.split(";");
        winStatus = winCondition.UNCONFIRMED;
	    height = Integer.parseInt(args[0]);
	    width = Integer.parseInt(args[1]);
	    numOfMines = Integer.parseInt(args[2]);
	    freeSpaces = Integer.parseInt(args[3]);
	    flagsLeft = Integer.parseInt(args[4]);
	    mineSet = Boolean.parseBoolean(args[5]);
	    
	    setLayout(new GridLayout(height, width));
	    cell = new Cell[height][width]; 
	    for(int i = 6; i < args.length; i++){
	        String[] cellString = args[i].split(",");
	        int x = Integer.parseInt(cellString[0].substring(5, cellString[0].length()));
	        int y = Integer.parseInt(cellString[1].substring(5, cellString[1].length()));
	        cell[y][x] = new Cell(args[i]);
	        cell[y][x].addMouseListener(this);
	        add(cell[y][x]);
	        
	    }
	    
	    
	    
	}
	private void populateGrid(int y, int x){
		int mineToGo= 0;
		Random r = new Random(System.currentTimeMillis());
		while(mineToGo < numOfMines){
			/* A good solution but a waist of resources and the seeds isn't "randomized"
			Random r = new Random();
			Random c = new Random();
			*/
			int rY = r.nextInt(height);
			int rX = r.nextInt(width);
			if(cell[rY][rX].isCovered() && !(rY == y && rX == x)){
				cell[rY][rX].setMine();
				mineToGo++;
			}

		}
		for(int row = 0; row < height; row++){
			for(int col = 0; col < width; col++){
				setMineCount(row,col);
			}
		}
	}
	
	private void setMineCount(int y, int x){
		if(!cell[y][x].isMined()){
			int adjacentMines = 0;
			if( x-1 >= 0 && y -1 >= 0) { 
				if(cell[y-1][x-1].isMined()) adjacentMines++;
			}
			if( y-1 >= 0 ) {
				if(cell[y-1][x].isMined()) adjacentMines++;
			}
			if( x+1 < width && y - 1 >= 0) {
				if(cell[y-1][x+1].isMined()) adjacentMines++;
			}
			if( x-1 >= 0) {
				if(cell[y][x-1].isMined()) adjacentMines++;
			}
			if( x+1 < width){
				if(cell[y][x+1].isMined()) adjacentMines++;
			}
			if( x-1 >= 0 && y + 1 < height){
				if(cell[y+1][x-1].isMined()) adjacentMines++;
			}
			if(y+1 < height){
				if(cell[y+1][x].isMined()) adjacentMines++;
			}
			if(x+1 < width && y + 1 < height){
				if(cell[y+1][x+1].isMined()) adjacentMines++;
			}
			cell[y][x].setValue(adjacentMines);
		}

	}
	
	public void unCoverCells(int y, int x){
        if(cell[y][x].isMined()){
            exploidMines();
        } else {
            cell[y][x].uncoverCell();
            if(cell[y][x].getValue() == 0){
                    if( (x-1 >= 0 && y -1 >= 0) && cell[y-1][x-1].isCovered()) unCoverCells(y-1,x-1);
                    if( y-1 >= 0 && cell[y-1][x].isCovered()) unCoverCells(y-1,x);
                    if( (x+1 < width && y - 1 >= 0) && cell[y-1][x+1].isCovered()) unCoverCells(y-1,x+1);
                    if( (x-1 >= 0) && cell[y][x-1].isCovered()) unCoverCells(y,x-1);
                    if(  x+1 < width && cell[y][x+1].isCovered()) unCoverCells(y,x+1);
                    
                    if( (x-1 >= 0 && y + 1 < height) && cell[y+1][x-1].isCovered())   unCoverCells(y+1,x-1);
                    
                    if  (y+1 < height && cell[y+1][x].isCovered()) unCoverCells(y+1,x);
                    if( (x+1 < width && y + 1 < height) && cell[y+1][x+1].isCovered()) unCoverCells(y+1,x+1);                
            }
        }
        checkWin();	        
	}
	
	private void exploidMines(){
	    winStatus = winCondition.FAILED;
		for(int i = 0; i < height; i++){
		    for(int j = 0; j < width; j++){
		        cell[i][j].setEnabled(false);
		        cell[i][j].setPressed(true);
		        if(cell[i][j].isMined()){
		            cell[i][j].uncoverCell();
		        }
		    }
		}
	}
	private void checkWin(){
	    //System.out.println(freeSpaces);
		if(freeSpaces >= 0){
		    freeSpaces--; 
		} else {
		    winStatus = winCondition.WON;
		}
	}
	public winCondition getWinStatus(){
		return winStatus;
	}
	public int getFlagsLeft(){
	    return flagsLeft;
	}
	public void setWinStatus(winCondition winStatus){
		this.winStatus = winStatus;
	}
	
	public void addMouseListener(MouseListener l){
	    for(int i = 0; i < height;i++){
	        for(int j = 0; j < width;j++){
	            cell[i][j].addMouseListener(l);
	        }
	    }
	}
	
	public void removeMouseListener(MouseListener l){
	    for(int i = 0; i < height; i++){
	        for(int j = 0;j < width; j++){
	            cell[i][j].removeMouseListener(l);
	        }
	    }
	}
	public boolean areMinesPopulated(){
	    return mineSet;
	}
	
	public String toString(){
	    String sGrid = new String();
	    for(int y = 0; y < height; y++){
	        for(int x = 0; x < width; x++){
	            sGrid += ";" + cell[y][x].toString();
	        }
	    }
	    return height + ";" + width + ";" + numOfMines + ";" + freeSpaces + ";" + flagsLeft + ";" + mineSet + sGrid;  
	}
	/**
	 * Mouse Events
	 */
	public void mouseClicked(MouseEvent e) {
	   
		if(e.getSource() instanceof minesweeper.Cell){	
		    
			Cell currentCell = (Cell)e.getSource();
			int x = currentCell.getX()  / currentCell.DEFAULT_SIZE;
			int y = currentCell.getY() / currentCell.DEFAULT_SIZE;
			if(winStatus == winCondition.UNCONFIRMED){
				if(e.getButton() == 1){
					if(currentCell.isCovered()){
	                    if(!mineSet){
	                        mineSet = true;
	                        populateGrid(y, x);
	                    }
	                    unCoverCells(y, x);					    
					}

				} else if (e.getButton() == 3){
				    System.out.println(flagsLeft);
				    if(currentCell.getState() == Cell.FLAGGED){
				        //System.out.println("Flagged");
				        if(flagsLeft <= numOfMines){
				            flagsLeft++;
		                    cell[y][x].SetFlag();
				        } 
				    } else if(currentCell.getState() == Cell.COVERED){
				        if(flagsLeft > 0){
	                        //System.out.println("Covered");
				            flagsLeft--;
		                    cell[y][x].SetFlag();
				        }
				    }

				}
			}
		}
		
	}
	
	public void mouseEntered(MouseEvent e) {	
	}
	
	public void mouseExited(MouseEvent e) {
	}
	
	public void mousePressed(MouseEvent e) {
	}
	
	public void mouseReleased(MouseEvent e) {
	}
	
	// Used for deciding if the game has won or lost
	private winCondition winStatus;

	private boolean mineSet;
	private Cell[][] cell;
	
	private int numOfMines;
	private int freeSpaces;
    private int height;
    private int width;
    
    private int flagsLeft;
	
	public static void main(String[] args){
		JFrame w = new JFrame("Minesweeper Test");
		w.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		Grid g = new Grid();
		System.out.println(g.toString());
		Grid g2 = new Grid(g.toString());
		w.add(g2);
		w.pack();
		w.setVisible(true);		
	}


}
